using System;
using System.Collections.Generic;
using System.Text;
using MacomberMap.Common.Database_Interface.CIM;
using System.Windows.Forms;
using MacomberMap.Common.Database_Interface.CIM.One_Line_Interface;
using System.Xml;
using MacomberMap.Common.Internals;
using System.IO;
using MacomberMap.Common.Database_Interface.CIM.Components;
using MacomberMap.Common.Components;
using System.Drawing;
using Oracle.DataAccess.Client;

namespace MacomberMap.Loader.Export
{
    /// <summary>
    /// This class handles exporting Macomber Map model files for the map to use
    /// </summary>
    public class MM_Exporter_MMXML : MM_Exporter
    {
        #region Variable declarations
        /// <summary>Our count of outgoing elements</summary>
        public float Total = 10;

        /// <summary>Our current element</summary>
        public float Current = 0;

        /// <summary>Track our current progress</summary>
        public override float Progress
        {
            get { return Current / Total; }
        }

        /// <summary>The target directory for the MM output</summary>
        public string TargetDirectory;

        /// <summary>The CIM database</summary>
        public MM_Database_CIM CIMDb;

        /// <summary>Our Oracle connector</summary>
        public OracleConnection NMMS_Oracle;

        /// <summary>Our collection of elements to process</summary>
        private List<CIM_Element> ElementsToProcess = new List<CIM_Element>();
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new MM XML exporter
        /// </summary>
        /// <param name="OutDirectory"></param>
        /// <param name="CIMDb"></param>
        /// <param name="Parent"></param>
        public MM_Exporter_MMXML(String OutDirectory, MM_Database_CIM CIMDb, ListView Parent)
            : base(Parent, "MM XML Exporter")
        {
            this.TargetDirectory = OutDirectory;
            this.CIMDb = CIMDb;

            //Make sure the new Oracle connector is opened

            XmlElement xElem = MM_Configuration.xConfig["MacomberMap_Loader"]["OneLines"];
            NMMS_Oracle = new OracleConnection(String.Format("User ID={0}; Password={1}; Data Source={2};", xElem.Attributes["NMMSUserName"].Value, xElem.Attributes["NMMSPassword"].Value, xElem.Attributes["NMMSDatabase"].Value));
            NMMS_Oracle.Open();

            //Build our list of elements to process
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:Company"));
            foreach (CIM_Element Elem in MM_Database_CIM.ElementsOfType("cim:Substation"))
                if (XmlConvert.ToBoolean(Elem["etx:Substation.forNetwork"]) && XmlConvert.ToBoolean(Elem["etx:Substation.forScada"]))
                    ElementsToProcess.Add(Elem);
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:ACLineSegment"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:SeriesCompensator"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:BusbarSection"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:SynchronousMachine"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:CombinedCyclePlant"));
            foreach (String ElemType in MM_Database_CIM.Types.Keys)
                if (ElemType.EndsWith("Load"))
                    ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType(ElemType));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:PowerTransformer"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:ShuntCompensator"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("etx:DCTie"));
            ElementsToProcess.AddRange(MM_Database_CIM.ElementsOfType("cim:Contingency"));


            this.Total = ElementsToProcess.Count;
        }

        /// <summary>
        /// Export our MM Model data
        /// </summary>
        public override void ProcessExporter()
        {
            Current = 0;
            using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(TargetDirectory, Path.GetFileNameWithoutExtension(MM_Database_CIM.CIMFileName)) + "-MacomberMap.MM_Model", Encoding.UTF8))
            {
                xW.WriteStartDocument(true);
                xW.Formatting = Formatting.Indented;
                xW.WriteStartElement("NetworkModel");
                xW.WriteAttributeString("SaveTime", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified));
                xW.WriteAttributeString("Model", Path.GetFileNameWithoutExtension(MM_Database_CIM.CIMFileName));
                //Now, print every county
                using (OracleCommand oCmd = new OracleCommand("SELECT * FROM MM_BOUNDARY", NMMS_Oracle))
                using (OracleDataReader oRd = oCmd.ExecuteReader())
                    while (oRd.Read())
                    {

                        xW.WriteStartElement(oRd["Name"].ToString().Equals("State", StringComparison.CurrentCultureIgnoreCase) ? "State" : "County");
                        xW.WriteAttributeString("Name", oRd["Name"].ToString());
                        xW.WriteAttributeString("ElemType", oRd["Name"].ToString().Equals("State", StringComparison.CurrentCultureIgnoreCase) ? "State" : "County");
                        xW.WriteAttributeString("TEID", "0");
                        xW.WriteAttributeString("Max", oRd["Max_X"].ToString() + "," + oRd["Max_Y"]);
                        xW.WriteAttributeString("Max_X", Convert.ToSingle(oRd["Max_X"]).ToString());
                        xW.WriteAttributeString("Max_Y", Convert.ToSingle(oRd["Max_Y"]).ToString());
                        xW.WriteAttributeString("Min_X", Convert.ToSingle(oRd["Min_X"]).ToString());
                        xW.WriteAttributeString("Min_Y", Convert.ToSingle(oRd["Min_Y"]).ToString());
                        xW.WriteAttributeString("Min", oRd["Min_X"].ToString() + "," + oRd["Min_Y"]);
                        xW.WriteAttributeString("Centroid_X", Convert.ToSingle(oRd["Centroid_X"]).ToString());
                        xW.WriteAttributeString("Centroid_Y", Convert.ToSingle(oRd["Centroid_Y"]).ToString());
                        xW.WriteAttributeString("Centroid", oRd["Centroid_X"].ToString() + "," + oRd["Centroid_Y"]);
                        StringBuilder sB = new StringBuilder();
                        byte[] inBytes = (byte[])oRd["Coordinates"];
                        for (int a = 0; a < inBytes.Length; a += 4)
                            sB.Append((a == 0 ? "" : ",") + BitConverter.ToSingle(inBytes, a).ToString());
                        xW.WriteAttributeString("Coordinates", sB.ToString());
                        xW.WriteEndElement();
                    }


                //Pull in our substation info
                Dictionary<MM_rdfID, PointF> SubCoordinates = new Dictionary<MM_rdfID,PointF>();
                Dictionary<MM_rdfID, String> SubLongNames = new Dictionary<MM_rdfID,string>();
                Dictionary<MM_rdfID, MM_Boundary> SubCounties = new Dictionary<MM_rdfID,MM_Boundary>();                    
                
                using (OracleCommand oCmd = new OracleCommand("SELECT * FROM MM_Substation", NMMS_Oracle))
                using (OracleDataReader oRd = oCmd.ExecuteReader())
                    while (oRd.Read())
                    {
                        PointF LatLng = new PointF(Convert.ToSingle(oRd["Longitude"]), Convert.ToSingle(oRd["Latitude"]));
                        MM_rdfID SubID = new MM_rdfID(oRd["rdfID"].ToString());
                        SubCoordinates.Add(SubID, LatLng);
                        SubLongNames.Add(SubID, oRd["LongName"].ToString());
                                                

                        //If our substation boundary is unknown, handle that too
                        if (oRd["Boundary"] is DBNull == false)
                            SubCounties.Add(SubID, MM_Repository.Counties[Convert.ToInt32(oRd["Boundary"])]);
                        else
                            foreach (MM_Boundary Bound in MM_Repository.Counties.Values)
                            if (Bound.HitTest(LatLng))
                                using (OracleCommand oCmd2 = new OracleCommand("UPDATE MM_SUBSTATION SET Boundary=:Bound WHERE rdfID=:rdfID", NMMS_Oracle))
                                {
                                    oCmd2.Prepare();
                                    oCmd2.Parameters.Add("Bound", Bound.Index);
                                    oCmd2.Parameters.Add("rdfID", oRd["rdfID"]);
                                    using (OracleDataReader oRd2 = oCmd2.ExecuteReader())
                                    { }
                                    SubCounties.Add(SubID, Bound);
                                    break;
                                }
                            
                            
                    }
                        

                
                //Now, go element by element, exporting data
                foreach (CIM_Element Elem in ElementsToProcess)
                {
                    Current++;
                    if (Elem.Type == "cim:Company")
                    {
                        
                        xW.WriteStartElement("Company");
                        String PhoneNumber =  Elem["etx:Company.PrimaryPhone"];
                        long TryParse, TryParse2;
                        if (long.TryParse(PhoneNumber, out TryParse))
                            if (PhoneNumber.Length == 10)
                                xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000"));
                            else
                                xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000 x\\." + new string('0',PhoneNumber.Trim().Length - 10)));
                        else if (PhoneNumber.IndexOf("x", StringComparison.CurrentCultureIgnoreCase) != -1 &&  long.TryParse(PhoneNumber.Split('x', 'X')[0].Trim(), out TryParse) && long.TryParse(PhoneNumber.Split('x', 'X')[1].Trim(), out TryParse2))
                            xW.WriteAttributeString("PrimaryPhone", TryParse.ToString("(000) 000-0000") + " x." + TryParse2.ToString());
                        else if (PhoneNumber == "(999) 999-9999 x____")
                            xW.WriteAttributeString("PrimaryPhone", " -Unknown- ");
                        else
                            xW.WriteAttributeString("PrimaryPhone",PhoneNumber);

                        if (long.TryParse(Elem["etx:Company.DUNS"], out TryParse))
                            xW.WriteAttributeString("DUNS", TryParse.ToString("#,##0"));
                        else
                            xW.WriteAttributeString("DUNS", Elem["etx:Company.DUNS"]);
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("ElemType", "Company");
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:Substation")
                    {
                        xW.WriteStartElement("Substation");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        PointF LatLng;
                        String LongName = MM_Repository.TitleCase(Elem["cim:IdentifiedObject.description"]);
                        if (String.IsNullOrEmpty(LongName))
                            LongName = MM_Repository.TitleCase(Elem.Name);
                        if (!SubCoordinates.TryGetValue(Elem.rdfID, out LatLng))
                        {
                            float Count = 0;
                            PointF TallyPoint = new PointF(), TempPoint;

                            foreach (CIM_Element Sub in Elem.Substations)
                                if (SubCoordinates.TryGetValue(Sub.rdfID, out TempPoint))
                                {
                                    TallyPoint.X += TempPoint.X;
                                    TallyPoint.Y += TempPoint.Y;
                                    Count++;
                                }

                            LatLng = new PointF(TallyPoint.X / Count, TallyPoint.Y / Count);
                            Console.WriteLine("Created new lat/lng for {0}: {1}, {2}", Elem.Name, LatLng.X, LatLng.Y);
                            SubCoordinates.Add(Elem.rdfID, LatLng);
                            SubLongNames.Add(Elem.rdfID, LongName);
                        }


                        xW.WriteAttributeString("LongName",  LongName);
                        xW.WriteAttributeString("Longitude", LatLng.X.ToString());
                        xW.WriteAttributeString("Latitude", LatLng.Y.ToString());
                        xW.WriteAttributeString("LatLong", LatLng.X.ToString() + "," + LatLng.Y.ToString());
                        Dictionary<String, List<CIM_Element>> ElemTally = new Dictionary<string, List<CIM_Element>>();
                        List<String> KVLevels = new List<string>();
                                                

                        foreach (CIM_Element VoltageLevel in Elem.Links)
                            if (VoltageLevel.Type == "cim:VoltageLevel")
                            {
                                string KV = GetKV(VoltageLevel);
                                if (!KVLevels.Contains(KV))
                                    KVLevels.Add(KV);                                
                                foreach (CIM_Element Equip in VoltageLevel.Links)
                                    if (Equip.Type == "cim:TransformerWinding")
                                        AddElement(ElemTally, "TransformerWinding", Equip);
                                    else if (Equip.Type.EndsWith("Load"))
                                        AddElement(ElemTally, "Load", Equip);
                                    else if (Equip.Type == "cim:SynchronousMachine")
                                        AddElement(ElemTally, "Unit", Equip);
                                    else if (Equip.Type == "cim:BusbarSection")
                                        AddElement(ElemTally, "BusbarSection", Equip);
                                    else if (Equip.Type == "cim:ShuntCompensator")
                                        AddElement(ElemTally, "ShuntCompensator", Equip);
                            }
                            else if (VoltageLevel.Type == "cim:PowerTransformer")
                                AddElement(ElemTally, "Transformer", VoltageLevel);

                        xW.WriteAttributeString("KVLevels", string.Join(",", KVLevels.ToArray()));

                        //TODO: Write county and load zone here
                        MM_Boundary FoundBound;
                        if (SubCounties.TryGetValue(Elem.rdfID, out FoundBound))
                            xW.WriteAttributeString("County", FoundBound.Name);
                        else
                            xW.WriteAttributeString("County", "STATE");

                        xW.WriteAttributeString("ElemTypes", GetElemTypes(ElemTally));
                        foreach (KeyValuePair<String, List<CIM_Element>> kvp in ElemTally)
                            xW.WriteAttributeString(kvp.Key + "s", GetTEIDs(kvp.Value));

                        xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "Substation");
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:ACLineSegment" || Elem.Type == "cim:SeriesCompensator")
                    {
                        xW.WriteStartElement("Line");

                        CIM_Element Sub1 = Elem.ACSegmentStations[0];
                        CIM_Element Sub2 = Elem.ACSegmentStations[1];
                        CIM_Element LineParent = Elem.Parse("cim:Line");
                      
                        if (LineParent.InLinks.Length == 1)
                            xW.WriteAttributeString("Name", MM_Repository.TitleCase(LineParent.Name));
                        else
                            xW.WriteAttributeString("Name", MM_Repository.TitleCase(LineParent.Name +  Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Substation1", Sub1.TEID);
                        xW.WriteAttributeString("Substation2", Sub2.TEID);
                        xW.WriteAttributeString("ConnectedStations", Sub1.TEID + "," + Sub2.TEID);
                        xW.WriteAttributeString("LineTEID", Elem.Parse("cim:Line").TEID);
                        PointF Sub1C = SubCoordinates[Sub1.rdfID];
                        PointF Sub2C = SubCoordinates[Sub2.rdfID];
                        xW.WriteAttributeString("Coordinates", Sub1C.X.ToString() + "," + Sub1C.Y.ToString() + "," + Sub2C.X.ToString() + "," + Sub2C.Y.ToString());                                                        
                        xW.WriteAttributeString("IsSeriesCompensator", (Elem.Type == "cim:SeriesCompensator" ? "True" : "False"));
                        xW.WriteAttributeString("IsMultipleSegment", Elem.Parse("cim:Line").InLinks.Length > 1 ? "True" : "False");
                        if (Elem.Owner != null)
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        else
                            xW.WriteAttributeString("Owner", LineParent.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "Line");
                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:BusbarSection")
                    {
                        CIM_Element Node = Elem.Parse("cim:Terminal>cim:ConnectivityNode");
                        xW.WriteStartElement("BusbarSection");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "BusbarSection");
                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                        xW.WriteAttributeString("Substation", Node.Substation.TEID);
                        
                        
                        xW.WriteStartElement("Node");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Node.Name));
                        xW.WriteAttributeString("TEID", Node.TEID);
                        xW.WriteAttributeString("Owner", Node.Owner.TEID);
                        xW.WriteAttributeString("Operator", Node.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "Node");
                        xW.WriteAttributeString("KVLevel", GetKV(Node.VoltageLevel));
                        xW.WriteAttributeString("Substation", Node.Substation.TEID);
                        xW.WriteEndElement();
                        
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:CombinedCyclePlant")
                    {
                        StringBuilder Configurations = new StringBuilder();
                        StringBuilder Units = new StringBuilder();

                        foreach (CIM_Element CCLogical in Elem.ParseMultiples("etx:LogicalConfiguration"))
                            Configurations.Append((Configurations.Length == 0 ? "" : ",") + CCLogical.Parse("etx:Configuration").Name);
                        foreach (CIM_Element CCUnit in Elem.Links)
                            if (CCUnit.Type.EndsWith("Unit"))
                                Units.Append((Units.Length == 0 ? "" : ",") + CCUnit.Parse("cim:SynchronousMachine").TEID);

                        xW.WriteStartElement("CombinedCycleConfiguration");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name.Substring(Elem.Substation.Name.Length + 1)));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "CombinedCycleConfiguration");
                        xW.WriteAttributeString("UnitType", "CombinedCycle");
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                        xW.WriteAttributeString("Units", Units.ToString());
                        xW.WriteAttributeString("Configurations", Configurations.ToString());
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:SynchronousMachine")
                    {
                        CIM_Element Phys = Elem.Parse("Unit");
                        xW.WriteStartElement("Unit");
                        String PhysType = Phys.Type.Split(':')[1];
                        for (int a = 1; a < PhysType.Length; a++)
                            if (Char.IsUpper(PhysType[a]))
                            {
                                xW.WriteAttributeString("UnitType", PhysType.Substring(0, a));
                                break;
                            }
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("UnitTEID", Phys.TEID);
                       if (Elem.Owner == null)
                           xW.WriteAttributeString("Owner", Elem.Substation .Owner.TEID);
                       else
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "Unit");
                        xW.WriteAttributeString("IsRC", Elem.Parse("Unit>CombinedCyclePlant") == null ? "False" : "True");
                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);

                        //Write out our logical units, if > 1
                        CIM_Element CC = Phys.Parse("cim:CombinedCyclePlant");
                        if (CC == null)
                        {
                            CIM_Element[] OwnerShareLimits = Phys.ParseMultiples("etx:OwnerShareLimits");
                            if (OwnerShareLimits.Length > 1)
                                foreach (CIM_Element Limit in OwnerShareLimits)
                                {
                                    xW.WriteStartElement("LogicalUnit");
                                    xW.WriteAttributeString("Name", MM_Repository.TitleCase(Limit.Name.Substring(Elem.Substation.Name.Length + 1)));
                                    xW.WriteAttributeString("TEID", Limit.TEID);
                                    xW.WriteAttributeString("Owner", Limit.Owner.TEID);
                                    xW.WriteAttributeString("Operator", Limit.Operator.TEID);
                                    xW.WriteAttributeString("ElemType", "LogicalUnit");
                                    xW.WriteAttributeString("UnitType", "Logical");
                                    xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                                    xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                                    xW.WriteEndElement();
                                }
                        }
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type.EndsWith("Load"))
                    {
                        bool IsLaar = Elem.Parse("etx:LoadResource") != null;
                        xW.WriteStartElement(IsLaar ? "LAAR" : "Load");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", IsLaar ? "LAAR" : "Load");
                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:PowerTransformer")
                    {
                        xW.WriteStartElement("Transformer");
                        CIM_Element[] Windings = Elem.TransformerWinding;
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        if (Elem.Owner != null)
                            xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        else
                            xW.WriteAttributeString("Owner", Elem.Substation.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "Transformer");
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                        xW.WriteAttributeString("Winding1", Windings[0].TEID);
                        xW.WriteAttributeString("KVLevel1", GetKV(Windings[0].VoltageLevel));
                        xW.WriteAttributeString("Winding2", Windings[1].TEID);
                        xW.WriteAttributeString("KVLevel2", GetKV(Windings[1].VoltageLevel));
                        xW.WriteAttributeString("Windings", GetTEIDs(Windings));
                        xW.WriteAttributeString("PhaseShifter", (Elem["cim:PowerTransformer.transformerType"].IndexOf("Phase") != -1 ? "True" : "False"));
                        xW.WriteEndElement();

                        foreach (CIM_Element XFw in Windings)
                        {
                            xW.WriteStartElement("TransformerWinding");
                            xW.WriteAttributeString("Name", MM_Repository.TitleCase(XFw.Name));
                            xW.WriteAttributeString("TEID", XFw.TEID);
                            if (XFw.Owner != null)
                                xW.WriteAttributeString("Owner", XFw.Owner.TEID);
                            else
                                xW.WriteAttributeString("Owner", XFw.Substation.Owner.TEID);
                            xW.WriteAttributeString("Operator", XFw.Operator.TEID);
                            xW.WriteAttributeString("ElemType", "TransformerWinding");
                            xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                            xW.WriteAttributeString("KVLevel", GetKV(XFw.VoltageLevel));
                            xW.WriteAttributeString("Transformer", Elem.TEID);
                            xW.WriteEndElement();
                        }
                    }
                    else if (Elem.Type == "cim:ShuntCompensator")
                    {
                        bool IsCapacitor = Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) > 0;
                        xW.WriteStartElement(IsCapacitor ? "Capacitor" : "Reactor");
                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", Elem.TEID);
                        xW.WriteAttributeString("Owner", Elem.Owner.TEID);
                        xW.WriteAttributeString("Operator", Elem.Operator.TEID);
                        xW.WriteAttributeString("ElemType", IsCapacitor ? "Capacitor" : "Reactor");
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                        xW.WriteAttributeString("KVLevel", GetKV(Elem.VoltageLevel));
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "etx:DCTie")
                    {
                        xW.WriteStartElement("DCTie");


                        //Retrieve our components
                        CIM_Element Unit = Elem.Parse("Unit");
                        CIM_Element Load = Elem.Parse("Load");
                        CIM_Element AssociatedLine = Elem.Parse("TieCorridor>TieLine>Terminal>ACLineSegment");
                            





                        xW.WriteAttributeString("Name", MM_Repository.TitleCase(Elem.Name));
                        xW.WriteAttributeString("TEID", "0");
                        xW.WriteAttributeString("Owner", AssociatedLine.Owner.TEID);
                        xW.WriteAttributeString("Operator", AssociatedLine.Operator.TEID);
                        xW.WriteAttributeString("ElemType", "DCTie");
                        xW.WriteAttributeString("Substation", Elem.Substation.TEID);
                        xW.WriteAttributeString("KVLevel", "DCTie");
                        xW.WriteAttributeString("Substation1", AssociatedLine.ACSegmentStations[0].TEID);
                        xW.WriteAttributeString("Substation2", AssociatedLine.ACSegmentStations[1].TEID);
                        xW.WriteAttributeString("Unit", Unit.TEID);
                        xW.WriteAttributeString("Load", Load.TEID);
                        xW.WriteAttributeString("LineTEID", AssociatedLine.Parse("cim:Line").TEID);
                        xW.WriteAttributeString("AssociatedLine", AssociatedLine.TEID);
                        xW.WriteEndElement();
                    }
                    else if (Elem.Type == "cim:Contingency")
                    {
                        xW.WriteStartElement("Contingency");
                        StringBuilder sB = new StringBuilder();
                        foreach (CIM_Element Sub in Elem.Substations)
                            sB.Append((sB.Length == 0 ? "" : ",") + Sub.TEID);
                        xW.WriteAttributeString("Name", Elem.Name);

                        xW.WriteAttributeString("Substations", sB.ToString());
                        xW.WriteEndElement();
                    }
                }
                xW.WriteEndElement();
                xW.WriteEndDocument();
            }

            //Now, update our model
            

        }

        /// <summary>
        /// Report the KV level of our element
        /// </summary>
        /// <param name="Elem"></param>
        /// <returns></returns>
        private string GetKV(CIM_Element Elem)
        {
            String VoltageLevel = Elem.Type == "cim:BaseVoltage" ? Elem.Name : Elem.Parse("cim:BaseVoltage").Name;
            if (VoltageLevel == "345 KV" || VoltageLevel == "138 KV" || VoltageLevel == "69 KV")
                return VoltageLevel;
            else
                return "Other KV";
        }

        /// <summary>
        /// Retrieve a list of TEIDs for an element
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private string GetTEIDs(IEnumerable<CIM_Element> list)
        {
            StringBuilder OutStr = new StringBuilder();
            foreach (CIM_Element Elem in list)
                OutStr.Append((OutStr.Length == 0 ? "" : ",") + Elem.TEID);
            return OutStr.ToString();
        }

        /// <summary>
        /// Retrieve our list of element types
        /// </summary>
        /// <param name="ElemTally"></param>
        /// <returns></returns>
        private string GetElemTypes(Dictionary<string, List<CIM_Element>> ElemTally)
        {
            List<String> OutList = new List<string>();
            foreach (KeyValuePair<String, List<CIM_Element>> kvp in ElemTally)
                if (kvp.Key == "ShuntCompensator")
                {
                    foreach (CIM_Element Elem in kvp.Value)
                        if (Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) > 0 && !OutList.Contains("Capacitor"))
                            OutList.Add("Capacitor");
                        else if (Convert.ToSingle(Elem["cim:ShuntCompensator.nominalMVAr"]) < 0 && !OutList.Contains("Reactor"))
                            OutList.Add("Reactor");
                }
                else if (kvp.Key == "Load")
                {
                    foreach (CIM_Element Elem in kvp.Value)
                        if (Elem.Parse("etx:LoadResource") != null && !OutList.Contains("LAAR"))
                            OutList.Add("LAAR");
                        else if (Elem.Parse("etx:LoadResource") == null && !OutList.Contains("Load"))
                            OutList.Add("Load");
                }
            return string.Join(",", new List<String>(ElemTally.Keys).ToArray());
        }

        /// <summary>
        /// Add an element to our dictionary
        /// </summary>
        /// <param name="ElemTally"></param>
        /// <param name="ElemType"></param>
        /// <param name="Equip"></param>
        private void AddElement(Dictionary<string, List<CIM_Element>> ElemTally, string ElemType, CIM_Element Equip)
        {
            List<CIM_Element> Elems;
            if (!ElemTally.TryGetValue(ElemType, out Elems))
                ElemTally.Add(ElemType, Elems = new List<CIM_Element>());
            Elems.Add(Equip);
        }
        #endregion
    }
}

